#ifndef STX_PLATFORM_POSIX_POSIX_THREAD_THIS_THREAD_HPP
#define STX_PLATFORM_POSIX_POSIX_THREAD_THIS_THREAD_HPP

#include <stx/platform/posix/thread/common.hpp>

namespace stx {

namespace posix {

namespace this_thread {

inline void exit(void* val = NULL)
{
    pthread_exit(val);
}

inline void test_cancel()
{
    pthread_testcancel();
}

//  Values:
//      PTHREAD_CANCEL_ENABLE
//      PTHREAD_CANCEL_DISABLE
inline void set_cancel_state(int state, int& old_state)
{
    int ret = pthread_setcancelstate(state, &old_state);
    if (ret) {
        STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_setcancelstate");
    }
}

inline void set_cancel_state(int state)
{
    int old_state;
    set_cancel_state(state, old_state);
}

//  Values:
//      PTHREAD_CANCEL_DEFERRED
//      PTHREAD_CANCEL_ASYNCHRONOUS
inline void set_cancel_type(int type, int& old_type)
{
    int ret = pthread_setcanceltype(type, &old_type);
    if (ret) {
        STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_setcanceltype");
    }
}

inline void set_cancel_type(int type)
{
    int old_type;
    set_cancel_type(type, old_type);
}

inline int concurrency()
{
    return pthread_getconcurrency();
}

inline void set_concurrency(int val)
{
    int ret = pthread_setconcurrency(val);
    if (ret) {
        STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_setconcurrency");
    }
}

inline void reset_concurrency()
{
    int ret = pthread_setconcurrency(0);
    if (ret) {
        STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_setconcurrency");
    }
}

inline pthread_t handle()
{
    return pthread_self();
}

class execute_and_cleanup
{
public:
    
    execute_and_cleanup(
        void (*execute_routine)(void*),
        void* execute_arg,
        void (*cleanup_routine)(void*),
        void* cleanup_arg,
        bool execute_cleanup)
    {
        pthread_cleanup_push(cleanup_routine, cleanup_arg);
        execute_routine(execute_arg);
        pthread_cleanup_pop(execute_cleanup ? 1 : 0);
    }
};

//  how:
//      SIG_BLOCK
//      SIG_SETMASK
//      SIG_UNBLOCK
#if STX_HAS_PTHREAD_SIGMASK

inline void set_signal_mask(int how, const sigset_t& set)
{
    int ret = pthread_sigmask(how, &set, NULL);
    if (ret) {
        STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_sigmask");
    }
}

inline sigset_t& get_signal_mask(sigset_t& set)
{
    int ret = pthread_sigmask(SIG_BLOCK, NULL, &set);
    if (ret) {
        STX_THROW_SYSTEM_ERROR_VAL(ret, "pthread_sigmask");
    }
    return set;
}

#endif

} // namespace this_thread

} // namespace posix

} // namespace stx

#endif // STX_PLATFORM_POSIX_POSIX_THREAD_THIS_THREAD_HPP
